﻿using GeneticAlgorithm.Demo.Common.Function.UnaryFunction;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GeneticAlgorithm.Demo.Common.Algorithm
{
    internal unsafe class Genetic
    {
        List<Gene> _genes;
        readonly Random _random;
        public Genetic()
        {
            this.Iterations = 200;
            this.PopulationSize = 100;
            this.CrossoverRate = 0.75d;
            this.MutationRate = 0.05d;
            this._random = new Random();
        }
        /// <summary>
        /// 迭代次数
        /// </summary>
        public uint Iterations { get; set; }
        /// <summary>
        /// 种群大小
        /// </summary>
        public uint PopulationSize { get; set; }
        /// <summary>
        /// 交叉率
        /// </summary>
        public double CrossoverRate { get; set; }
        /// <summary>
        /// 变异率
        /// </summary>
        public double MutationRate { get; set; }
        public IUnaryFunction Function { get; set; }
        public List<Gene> Run()
        {
            Stopwatch sw = Stopwatch.StartNew();
            // 种群初始化
            InitPopulation();

            List<Gene> bestGenes = new List<Gene>((int)PopulationSize);
            // 迭代
            for (int i = 0; i < this.Iterations; i++)
            {
                // 选择
                Selection();
                // 交叉
                Crossover();
                // 变异
                Mutation();
                // 保留精英
                _genes.AddRange(_elitists);
                // 过滤
                Filterate();
                if (_genes.Count != 0)
                {
                    var best = GetBestGene();
                    bestGenes.Add(best);
                }
            }
            // 0-1化
            MinMaxScaling(bestGenes);

            sw.Stop();
            Trace.WriteLine($"耗时：{sw.ElapsedMilliseconds}ms");
            return bestGenes;
        }
        private void MinMaxScaling(List<Gene> bestGenes)
        {
            var max = bestGenes.Max(t => t.Fitness);
            var min = bestGenes.Min(t => t.Fitness);
            foreach (var item in bestGenes)
            {
                if (item.Fitness < min) continue;
                item.Fitness = Math.Round((item.Fitness - min) / (max - min),Function.Accuracy);
            }
        }
        private void Filterate()
        {
            for (int i = 0; i < _genes.Count; i++)
            {
                var gene = _genes[i];
                this.Fitness(gene);

                if (gene.DisplayValue < Function.Range.lower || gene.DisplayValue > Function.Range.upper)
                {
                    _genes.Remove(gene);
                    continue;
                }
            }
        }
        #region 种群初始
        private void InitPopulation()
        {
            _genes = new List<Gene>((int)this.PopulationSize);
            for (int i = 0; i < PopulationSize; i++)
            {
                double x = GenerateRandomNumberInRange(_random, Function.Range.lower, Function.Range.upper, Function.Accuracy);
                var gene = new Gene(x);
                // 编码
                gene.Code(Function.Range.lower, Function.Range.upper, Function.Accuracy, Function.GeneMinLength);
                // 计算r值（适应度值）
                this.Fitness(gene);
                _genes.Add(gene);
            }
        }
        double GenerateRandomNumberInRange(Random random, double min, double max, int decimalPlaces)
        {
            double randomNumber = min + (max - min) * random.NextDouble();
            double multiplier = Math.Pow(10, decimalPlaces);
            return Math.Round(randomNumber * multiplier) / multiplier;
        }
        #endregion
        #region 适度函数
        private void Fitness(Gene gene)
        {
            var y = Function.Func(gene.DisplayValue);
            double value;
            if (Function.Solve == Common.Function.SolveType.Max)
                value = Math.Pow(2, y);
            else
                value = Math.Pow(0.5d, y);

            gene.Fitness = value;
            gene.Y = y;
        }
        private Gene? GetBestGene()
        {
            Gene max = _genes[0];
            for (int i = 1; i < _genes.Count; i++)
            {
                var temp = _genes[i];
                if (max.Fitness < temp.Fitness)
                    max = temp;
            }

            return max.Clone() as Gene;
        }
        #endregion
        #region 选择
        private List<Gene> _elitists;
        private void Selection()
        {
            const int elitistCount = 1;
            _elitists = _genes.OrderByDescending(x => x.Fitness).Take(elitistCount).Select(t => t.Clone() as Gene).ToList();
            _genes = Select(_genes, _genes.Count - elitistCount);
        }
        public virtual List<Gene> Select(List<Gene> array, int n)
        {
            double sumFitness = SumFitness(array);
            double[] probabilities = Probabilities(array, sumFitness);
            List<Gene> genes = new List<Gene>(array.Count);
            for (int i = 0; i < n; i++)
            {
                genes.Add(SelectElementByProbability(array, probabilities));
            }
            return genes;
        }
        protected double[] Probabilities(List<Gene> array, double sumFitness)
        {
            double[] probabilities = new double[array.Count];
            for (int i = 0; i < array.Count; i++)
            {
                var gene = array[i];
                probabilities[i] = gene.Fitness / sumFitness;
            }
            return probabilities;
        }
        protected double SumFitness(List<Gene> array)
        {
            double sumFitness = 0d;
            for (int i = 0; i < array.Count; i++)
            {
                sumFitness += array[i].Fitness;
            }
            return sumFitness;
        }
        protected virtual Gene SelectElementByProbability(List<Gene> array, double[] probabilities)
        {
            double randomValue = _random.NextDouble();
            Gene gene = array[0];
            double cumulativeProbability = 0.0;
            for (int i = 0; i < array.Count; i++)
            {
                cumulativeProbability += probabilities[i];
                if (randomValue <= cumulativeProbability)
                {
                    gene= array[i];
                    break;
                }
            }
            return gene.Clone() as Gene;
        }
        #endregion
        #region 交叉
        private void Crossover()
        {
            var geneLength = Function.GeneMinLength-1;
            for (int i = 0; i < _genes.Count - 1; i++)
            {
                var curRote = _random.NextDouble();
                if (this.CrossoverRate < curRote) continue;
                var parent1 = _genes[i];
                var parent2 = _genes[i + 1];
                var index = _random.Next(0, geneLength);

                (parent1.Genotype[index], parent2.Genotype[index]) = (parent2.Genotype[index], parent1.Genotype[index]);
                (parent1.Genotype[index + 1], parent2.Genotype[index + 1]) = (parent2.Genotype[index + 1], parent1.Genotype[index + 1]);

                parent1.Decode(Function.Range.lower, Function.Range.upper, Function.Accuracy);
                parent2.Decode(Function.Range.lower, Function.Range.upper, Function.Accuracy);
            }
        }
        #endregion
        #region 变异
        private void Mutation()
        {
            var geneLength = Function.GeneMinLength;
            for (int i = 0; i < _genes.Count; i++)
            {
                var curRote = _random.NextDouble();
                if (this.MutationRate < curRote) continue;
                var parent = _genes[i];
                // 单点变异
                //var j = _random.Next(0, geneLength);
                //parent.Genotype[j] = (byte)(parent.Genotype[j] ^ 1);// 取反
                //parent.Decode(Function.Range.lower, Function.Range.upper, Function.Accuracy);

                // 多点变异
                int count = _random.Next(1, 5);
                for (int k = 0; k < count; k++)
                {
                    var j = _random.Next(0, geneLength);
                    parent.Genotype[j] = (byte)(parent.Genotype[j] ^ 1);// 取反
                    parent.Decode(Function.Range.lower, Function.Range.upper, Function.Accuracy);
                }
            }
        }
        #endregion
    }
}
